/**
 * CustomerServlet
 * Servlet for an Ecom customer.
 * @author    Fabienne Boyer - Didier Donsez - may 2006
 */
package ecom.servlets;

import ecom.cart.Cart;
import ecom.cart.CartLocal;
import ecom.entity.Adress;
import ecom.entity.Category;
import ecom.entity.Country;
import ecom.entity.Customer;
import ecom.entity.Product;
import ecom.entity.ProductStore;
import ecom.entity.Store;
import ecom.exception.EcomException;
import ecom.session.EcomAdminRemote;
import ecom.session.EcomCustomerRemote;
import ecom.session.EcomEuroConverterLocal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ejb.EJB;
import javax.servlet.ServletConfig;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class CustomerServlet extends HttpServlet {

    public final static String CART_LOCAL = "cart";
    public final static String CART_ID = "cartID";
    public final static String EURO_CONVERTER_NAME = "eurConverter";
    public final static String LANGUAGE_NAME = "language";
    @EJB
    private EcomCustomerRemote customerBean;
    @EJB
    private EcomAdminRemote adminBean;
    @EJB
    private EcomEuroConverterLocal euroConverter;
    @EJB
    private CartLocal cartLocal;
    /**
     * Serializable class uid.
     */
    private static final long serialVersionUID = 6893863749912962928L;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        // on set le euro converter
        getServletContext().setAttribute(EURO_CONVERTER_NAME, euroConverter);

        getServletContext().setAttribute(LANGUAGE_NAME, "fr");
    }

    /**
     * Called by the server (via the service method) to allow a servlet to
     * handle a GET request.
     * @param request an HttpServletRequest object that contains the request
     * the client has made of the servlet
     * @param response an HttpServletResponse object that contains the
     * response the servlet sends to the client
     * @throws IOException if an input or output error is detected when the
     * servlet handles the GET request
     * @throws ServletException if the request for the GET could not be handled
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        String userPath = request.getServletPath();
        HttpSession session = request.getSession();
        List<Product> productList = null;
        boolean simpleRedirect = false; // redirige par defaut vers les jsps

        if (userPath.equals(PageName.PAGE_PRODUCTS)) {

            // Gestion des erreur lorsque l'utilisateur passe par l'url et demande une fiche produit inexistante
            String flagError = (String) request.getParameter("flag");
            if (flagError != null && "true".equals(flagError)) {
                request.setAttribute("errorMessage", "Le produits demandé n'existe pas ! Eviter de passer directement par l'URL...");
            }

            productList = customerBean.listProducts();
            // place les produits dans la session
            request.setAttribute("productList", productList);
        } else if (userPath.equals(PageName.PAGE_PRODUCT)) {
            // recuperation d'une fiche produit
            Integer idProduct = Integer.parseInt(request.getParameter("idProduct"));
            Product p = customerBean.findProductById(idProduct);
            request.setAttribute("product", p);
        } else if (userPath.equals(PageName.PAGE_STORES)) {
            // vin par boutiques
            String storeId = request.getParameter("idStore");
            Integer storeIdInt = new Integer(0);

            if (null != storeId && !"".equals(storeId)) {
                storeIdInt = Integer.parseInt(storeId);
            }

            List<Store> stores = customerBean.listStores();
            List<ProductStore> productStoreList = customerBean.listProductsStore(storeIdInt);

            // on remanie la liste pour avoir une liste de produits
            for (ProductStore ps : productStoreList) {

                // on instantie la liste si elle vaut null
                if (null == productList) {
                    productList = new ArrayList<Product>();
                }

                productList.add(ps.getProduct());
            }

            request.setAttribute("productList", productList);
            request.setAttribute("current", storeIdInt);
            request.setAttribute("stores", stores);



        } else if (userPath.equals(PageName.PAGE_CATEGORY)) {
            // vin par region
            String categoryId = request.getParameter("idCategory");
            Integer categoryIdInt = new Integer(0);

            if (null != categoryId && !"".equals(categoryId)) {
                categoryIdInt = Integer.parseInt(categoryId);
            }

            productList = customerBean.listProductsByCategory(categoryIdInt);
            List<Category> categories = customerBean.listCategories();

            request.setAttribute("productList", productList);
            request.setAttribute("current", categoryIdInt);
            request.setAttribute("categories", categories);
        } else if (userPath.equals(PageName.PAGE_CART)) {
            // suppression des items du panier si presence du param clear
            Integer cartId = (Integer) session.getAttribute(CART_ID);
            if (cartId == null) {
                throw new ServletException("Vous n'avez pas accès à cette page si vous n'avez pas encore ajouter un produit au panier. Vous n'avez pas initialiser le panier correctement... ");
            }

            String clear = request.getParameter("clear");
            if ((clear != null) && clear.equals("true")) {
                cartLocal.clear(cartId);
                session.setAttribute("cartNbItems", 0);
            }

            request.setAttribute(CART_LOCAL, cartLocal.getCart(cartId));

        } else if (userPath.equals("/chooseLanguage")) {
            // recupere le langage
            String language = request.getParameter(LANGUAGE_NAME);
            session.setAttribute(LANGUAGE_NAME, language);

            // on recupere la vue d'ou l'on vient
            String userView = (String) session.getAttribute("view");
            Logger.getLogger(CustomerServlet.class.getName()).log(Level.SEVERE, "UserView=" + userView, userView);
            if ((userView != null)
                    && (!userView.equals("/index"))) {     // on gere specifiquement l'index dont la jsp se trouve a la racine
                userPath = userView;
            } else {

                // Si la precedente vue ne peut etre determiner on redirige vers l'index
                try {
                    request.getRequestDispatcher("/index.jsp").forward(request, response);
                } catch (Exception ex) {
                    Logger.getLogger(CustomerServlet.class.getName()).log(Level.SEVERE, null, ex);
                }
                return;
            }
        } else if (userPath.equals(PageName.PAGE_AUTHENTICATE)) {
            // page d'authentification lors du process d'achat
            List<Country> countryList = customerBean.listCountry();
            request.setAttribute("countryList", countryList);

        } else if (userPath.equals(PageName.ACTION_LOGOUT)) {
            // gestion de la deconnexion du client
            session.removeAttribute("customer");
            session.removeAttribute(CART_ID);
            session.removeAttribute("cartNbItems");
            request.setAttribute("infoMessage", "Vous avez été deconnecté.");
            userPath = "/index";
        } else if (userPath.equals(PageName.ACTION_ADD_ADRESS)) {

            String currentView = (String) session.getAttribute("view");

            
            // on veut afficher le formulaire donc
            //      - on ajoute un boolean pour afficher le formulaire
            //      - on ajoute la liste des pays
            
            if (currentView.equals(PageName.PAGE_CHECKOUT)) {
                Customer c = (Customer) session.getAttribute("customer");
                calculateAndSetAttributeForCheckout(request, session, c);
            }
            request.setAttribute("countryList", customerBean.listCountry());
            request.setAttribute("addAdress", true);

            // on redirige vers la bonne page
            userPath = currentView;

        } else if (userPath.equals(PageName.PAGE_CHECKOUT)) {
            Customer c = (Customer) session.getAttribute("customer");
            Integer cartId = (Integer) session.getAttribute(CART_ID);

            // on verifie que nous ne sommes pas dans de acces URL en etant precis
            if (c == null || cartId == null || cartLocal.getItems(cartId).isEmpty()) {
                // on bloque l'acces a la page par URL
                throw new ServletException("Vous n'avez pas accès à cette page par l'URL !!!");
            }

            // calcul de l'effet sur le compte utilisateur
            boolean flagAchat = true;
            double customerBalance = c.getAccount().getBalance();
            double total = cartLocal.getSubtotal(cartId);

            double result = customerBalance - total;

            if (result < 0) {
                // impossible d'effectuer l'achat
                flagAchat = false;
                request.setAttribute("errorMessage", "Le solde de votre compte est insuffisant. Veuillez faire un dépôt avant achat.");
            }

            request.setAttribute("cart", cartLocal.getCart(cartId));
            request.setAttribute("result", result); // ajout de l'attribut result pour connaitre l'effet sur la balance
            request.setAttribute("flagAchat", flagAchat); // ajout du flag achat pour savoir si le client peut faire l'achat

        } else if (userPath.equals(PageName.PAGE_ACCOUNT)) {
            // on s'assure que le client est deja logguer
            // sinon il s'agit d'un acces via URL
            Customer currentCustomer = (Customer) session.getAttribute("customer");
            if (null == currentCustomer) {
                throw new ServletException("Veuillez vous authentifier avant d'accéder à la page du compte.");
            }
        }

        // redirige la page en fonction de userPath
        redirection(request, response, userPath, simpleRedirect);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");

        String userPath = request.getServletPath();
        HttpSession session = request.getSession();
        boolean simpleRedirect = false;

        // on ajoute un produit au cart 
        if (userPath.equals(PageName.ACTION_ADD_TO_CART)) {

            Integer cartId = (Integer) session.getAttribute(CART_ID);

            if (cartId == null) {
                // Creation du Cart sur le serveur 
                // Gestion du cart avec une cle en session
                cartId = cartLocal.createCart(new Cart());
                session.setAttribute(CART_ID, cartId);
            }

            Integer productId = Integer.parseInt(request.getParameter("productId"));
            Integer storeId = Integer.parseInt(request.getParameter("storeId"));
            double price = Double.parseDouble(request.getParameter("price"));
            Product p = customerBean.findProductById(productId);

            cartLocal.addItem(cartId, p, price, storeId);
            userPath = PageName.PAGE_PRODUCTS;

            // on passe un flag pour savoir si l'ajout s'est bien passe
            // on ajoute un objet Cart pour l'affichage
            request.setAttribute("cart", cartLocal.getCart(cartId));
            request.setAttribute("infoMessage", p.getName() + " a bien été ajouté au panier");
            request.setAttribute("productList", customerBean.listProducts());

            session.setAttribute("cartNbItems", cartLocal.getNumberOfItems(cartId));
        } else if (userPath.equals(PageName.ACTION_ADD_ADRESS)) {
            // on recupere la vue courante
            String currentView = (String) session.getAttribute("view");

            // Ajout d'une adresse au compte client
            // on recupere les params
            String street = request.getParameter("street");
            String zipcode = request.getParameter("zipcode");
            String city = request.getParameter("city");
            String idCountry = request.getParameter("country");

            // c'est la creation de l'adresse
            Adress newAdress = new Adress();
            newAdress.setCity(city);
            newAdress.setStreet(street);
            newAdress.setZipcode(zipcode);
            newAdress.setCountry(new Country(Integer.parseInt(idCountry))); // on ajoute juste un country avec l'id recuperer

            // on ajoute l'adresse au customer
            Customer c = (Customer) session.getAttribute("customer");
            c = customerBean.addAdressToCustomer(newAdress, c);

            // on ajoute un message et on set la page a account
            request.setAttribute("infoMessage", "Une nouvelle adresse a été ajoutée à votre profil client.");
            
            if(currentView.equals(PageName.PAGE_CHECKOUT)) {
                calculateAndSetAttributeForCheckout(request, session, c);
            }
            userPath = currentView;
        } else if (userPath.equals(PageName.ACTION_CREATE_ACCOUNT)) {
            // Creation d'un compte customer pour la premiere fois
            String lastName = (String) request.getParameter("lastName");
            String firstName = (String) request.getParameter("firstName");
            String street = (String) request.getParameter("street");
            String zipCode = (String) request.getParameter("zipcode");
            String city = (String) request.getParameter("city");
            Integer idCountry = Integer.parseInt(request.getParameter("country"));
            String email = (String) request.getParameter("email");
            String password = (String) request.getParameter("password");

            String iban = (String) request.getParameter("iban");

            if (verifParamsForCustomerCreation(lastName, firstName, street, zipCode, city, idCountry, email, password, iban)) {
                adminBean.createCustomer(lastName, firstName, email, password, street, zipCode, city, idCountry, iban, 2000);

                // Recuperation de l'utilisateur cree
                Customer c = customerBean.login(email, password);

                // on verifie que c est bien recuperer
                if (c == null) {
                    throw new ServletException("Erreur lors de la creation du compte");
                }

                calculateAndSetAttributeForCheckout(request, session, c);
                session.setAttribute("customer", c);
                userPath = PageName.PAGE_CHECKOUT;

            } else {
                // erreur des params relancer l'utilisateur
                // il faut recuperer les country         
                List<Country> countryList = customerBean.listCountry();
                request.setAttribute("countryList", countryList);
                request.setAttribute("errorMessage", "Impossible de créer le compte. Vérifiez la saisie des informations :"
                        + "(le mail respecte bien le format des mails, le numéro de compte respecte le standard IBAN)");
                userPath = PageName.PAGE_AUTHENTICATE;
            }

        } else if (userPath.equals(PageName.ACTION_LOGIN)) {

            String currentView = (String) session.getAttribute("view");

            // Connection d'un USER
            String login = (String) request.getParameter("email");
            String password = (String) request.getParameter("password");

            if ("".equals(login) || "".equals(password) || !isEmailAdress(login)) {
                request.setAttribute("errorMessage", "Informations saisies incorrectes, vérifier le mail et le password.");
                userPath = currentView;
            } else {
                Customer c = customerBean.login(login, password);

                if (c == null) {
                    request.setAttribute("errorMessage", "Cet utilisateur n'existe pas. Veuillez réessayer ou créer un nouveau compte");
                    userPath = "/index";
                } else if (!c.getPassword().equals(password)) {
                    request.setAttribute("errorMessage", "Vérifier vos identifiants. Mot de passe faut pour " + login);
                    userPath = "/index";
                } else {
                    session.setAttribute("customer", c);

                    // on redirige l'utilisateur vers la vue checkout si il se log pendant le process d'achat
                    // sinon vers sa page du compte
                    if (currentView.equals(PageName.PAGE_AUTHENTICATE)) {

                        calculateAndSetAttributeForCheckout(request, session, c);
                        userPath = PageName.PAGE_CHECKOUT;
                    } else {
                        userPath = PageName.PAGE_ACCOUNT;
                    }
                }
            }


        } else if (userPath.equals(PageName.ACTION_UPDATE_CART)) {
            // recuperation du cartId
            Integer cartId = (Integer) session.getAttribute(CART_ID);

            if (cartId == null) {
                throw new ServletException("Vous n'avez pas accès à cette page si vous n'avez pas encore ajouter un produit au panier. Vous n'avez pas initialiser le panier correctement... ");
            }

            // mise ajour du panier
            String productId = request.getParameter("productId");
            String storeId = request.getParameter("storeId");
            String quantity = request.getParameter("quantity");

            boolean invalidEntry = validateQuantity(productId, quantity);

            if (!invalidEntry) {
                Product product = customerBean.findProductById(Integer.parseInt(productId));
                cartLocal.update(cartId, product, storeId, quantity);
                request.setAttribute("infoMessage", "Quantité modifié avec succès.");
                session.setAttribute("cartNbItems", cartLocal.getNumberOfItems(cartId));
            } else {
                request.setAttribute("errorMessage", "Saisie inccorect! l'état du panier n'a pas été modifié.");
            }
            
            request.setAttribute(CART_LOCAL, cartLocal.getCart(cartId));
            userPath = PageName.PAGE_CART;

        } else if (userPath.equals(PageName.ACTION_PURCHASE)) {

            // on recupere l'adresse de livraison
            int idAdress = Integer.parseInt(request.getParameter("idAdress"));

            // on recupere le customer en session
            Customer c = (Customer) session.getAttribute("customer");

            // on recupere l'adresse a partir de l'id
            Adress adress = customerBean.findAdressById(idAdress);

            // on recupere l'id du cart
            Integer cartId = (Integer) session.getAttribute(CART_ID);

            if (cartId == null || c == null) {
                throw new ServletException("Vous n'avez pas accès à cette page si vous n'avez pas encore ajouter un produit au panier. Vous n'avez pas initialiser le panier correctement... ");
            }

            try {
                // on crée l'achat dans un contexte
                customerBean.purchase(cartLocal.getItems(cartId), c, adress);

                // on vide le panier
                cartLocal.clear(cartId);
                session.setAttribute("cartNbItems", 0);
                
                // on recharge le customer pour avoir la derniere commande
                session.setAttribute("customer", customerBean.login(c.getEmail(), c.getPassword()));
                request.setAttribute("infoMessage", "Votre achat a été effectué avec succès.");

            } catch (EcomException ex) {
                request.setAttribute("errorMessage", ex.getErrorMsg());
            }

            // on redirige vers la vue account
            userPath = PageName.PAGE_ACCOUNT;

        }

        // redirige la page en fonction de userPath
        redirection(request, response, userPath, simpleRedirect);
    }

    /**
     * Permet de rediriger le controller vers la bonne jsp
     * Les JSPs sont localisees dans le dossier WEB-INF
     * @param request
     * @param response
     * @param userPath 
     * @param simpleRedirect : permet de rediriger directement en passant par le web.xml et non par les pages jsp physiques
     */
    private void redirection(HttpServletRequest request, HttpServletResponse response, String userPath, boolean simpleRedirect) {
        String url;

        if (simpleRedirect) {
            // redirige avec le web.xml
            url = userPath;
        } else {
            // redirige vers une jsp physique   
            // on utilise un requestdispatcher pour changer de vue
            if (userPath.equals("/index") || userPath.equals("index")) {
                url = "/index.jsp";
            } else {
                url = "/WEB-INF/view" + userPath + ".jsp";
            }
        }

        try {
            request.getRequestDispatcher(url).forward(request, response);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Permet de verifier que les param sont coherent au niveau de la vue
     * @param lastName
     * @param firstName
     * @param street
     * @param zipCode
     * @param city
     * @param idCountry
     * @param email
     * @param password
     * @param iban
     * @return 
     */
    private boolean verifParamsForCustomerCreation(String lastName, String firstName, String street, String zipCode, String city, Integer idCountry, String email, String password, String iban) {
        boolean flag = true;

        flag = lastName != null && !"".equals(lastName);
        flag = firstName != null && !"".equals(firstName);
        flag = street != null && !"".equals(street);
        flag = zipCode != null && !"".equals(zipCode);
        flag = city != null && !"".equals(city);
        flag = idCountry != null;
        flag = email != null && !"".equals(email) && isEmailAdress(email);
        flag = password != null && !"".equals(password);
        flag = iban != null && !"".equals(iban) && isIban(iban);

        return flag;
    }

    /**
     * Valide une adresse mail
     * @param email
     * @return 
     */
    public static boolean isEmailAdress(String email) {
        Pattern p = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$");
        Matcher m = p.matcher(email.toUpperCase());
        return m.matches();
    }

    /**
     * Valide un Iban
     * @param iban
     * @return 
     */
    public static boolean isIban(String iban) {
        Pattern p = Pattern.compile("[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}");
        Matcher m = p.matcher(iban.toUpperCase());
        return m.matches();
    }

    /**
     * Verifie que le chiffre est coherent et que le stock est disponible
     * @param productId
     * @param quantity
     * @return 
     */
    private static boolean validateQuantity(String productId, String quantity) {
        boolean errorFlag = false;

        if (!productId.isEmpty() && !quantity.isEmpty()) {

            int i = -1;

            try {
                i = Integer.parseInt(quantity);
            } catch (NumberFormatException nfe) {
                System.out.println("L'utilisateur n'a pas saisie un chiffre");
            }

            if (i < 0 || i > 99) {

                errorFlag = true;
            }
        }

        return errorFlag;
    }

    
    /**
     * Factorise du code
     * @param request
     * @param session
     * @param c 
     */
    private void calculateAndSetAttributeForCheckout(HttpServletRequest request, HttpSession session, Customer c) {
        // on recupere les info necessaires pour cette page
        Integer cartId = (Integer) session.getAttribute(CART_ID);

        // calcul de l'effet sur le compte utilisateur
        boolean flagAchat = true;
        double customerBalance = c.getAccount().getBalance();
        double total = cartLocal.getSubtotal(cartId);

        double result = customerBalance - total;

        if (result < 0) {
            // impossible d'effectuer l'achat
            flagAchat = false;
            request.setAttribute("errorMessage", "Le solde de votre compte est insuffisant. Veuillez faire un dépôt avant achat.");
        }

        request.setAttribute(CART_LOCAL, cartLocal.getCart(cartId));
        request.setAttribute("result", result); // ajout de l'attribut result pour connaitre l'effet sur la balance
        request.setAttribute("flagAchat", flagAchat); // ajout du flag achat pour savoir si le client peut faire l'achat
    }
}
